package com.cx.common.rhino;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;

import javax.script.ScriptException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;


public class Rhino {

    /**
     * ThreadLocal
     */
    private static final ThreadLocal<Scriptable> THREAD_LOCALS = new
            ThreadLocal<Scriptable>();
    /**
     * 公式缓存
     */
    private static final ConcurrentMap<String, Script> SCRIPT_CACHE = new
            ConcurrentHashMap<String, Script>();

    public static void main(String[] args) throws ScriptException {
        String expression = "Math.max(n1-n3*Math.floor(Math.sqrt(n2))-n3,n3)";
        String expression2 = "Math.max(n1-n3*Math.floor(Math.sqrt(n2))-n3,n3)";
        String expression4 = "Math.max(unitPrice * clientNum * faceOption,totalBeginPrice)";

//        String expression5 = "n5+n8+n4+n1";
        String expression5 = "n1+n2+n3+n4";
        int n1 = 1;
        int n2 = 2;
        int n3 = 3;
        int n4 = 1;
        int n5 = 2;
        int n6 = 3;
        int n7 = 1;
        int n8 = 3;
        int n9 = 3;
        int n10 = 2;

//        String expression4 = "Math.max(unitPrice * clientNum * faceOption,totalBeginPrice)";

        double unitPrice = 3.0;
        double clientNum = 5;
        int faceOption = 2;
        double totalBeginPrice = 6;

        /*double unitPrice = 3.0;
        double clientNum = 5;
        int faceOption = 2;
        double totalBeginPrice = 6;*/

        String expression3 = "(((n1*n2)/(n3*2) +(120/n4*n1))* n6 * n7 * n8)+ n9 +n10";

//        double value1 = RhinoHelper.invoke(expression2, n1, n2, n3, n4, n5, n6, n7,
//                n8, n9, n10).doubleValue();

//        double value2 = RhinoHelper.invoke(expression5, n5, n8, n4, n1).doubleValue();

        double craftPrice = RhinoHelper.invoke(expression4, unitPrice, clientNum, faceOption, totalBeginPrice).doubleValue();
//        System.out.println(value1);
//        System.out.println(value2);
        System.out.println(craftPrice);

        // ScriptEngineManager factory = new ScriptEngineManager();
        // ScriptEngine engine = factory.getEngineByName("JavaScript");
        // HelloWorld hello = new HelloWorld();
        // engine.put("script_hello", hello);
        // engine.eval("script_hello.sayHello()");
    }

    private static Scriptable getScope() {
        Scriptable scope = THREAD_LOCALS.get();
        if (scope == null) {
            ContextFactory global = ContextFactory.getGlobal();
            Context context = global.enterContext();
            context.setWrapFactory(new MapWarperFactory());

            scope = context.initStandardObjects();
            THREAD_LOCALS.set(scope);
        }
        return scope;
    }

    /**
     * 计算公式获得计算结果
     *
     * @param expression 公式
     * @param ctx        上下文
     * @return {@link Object} 返回值
     */
    public static Object invoke(String expression, Map<String, ?> ctx) {
        Script exp = SCRIPT_CACHE.get(expression);
        Context context = Context.enter();
        try {
            if (exp == null) {
                exp = context.compileString(expression, "<expr>", -1, null);
                SCRIPT_CACHE.putIfAbsent(expression, exp);
            }

            exp = SCRIPT_CACHE.get(expression);
            Scriptable args = context.newObject(getScope());
            if (ctx != null && !ctx.isEmpty()) {
                for (Entry<String, ?> e : ctx.entrySet()) {
                    args.put(e.getKey(), args, e.getValue());
                }
            }
            return exp.exec(context, args);
        } finally {
            Context.exit();
        }
    }
}
